﻿#include "net.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <string.h>
#include <stdlib.h>
#include <ifaddrs.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
#include <net/if.h>

int NetWork::NetSplitStr(char* inStr, char cc, int maxLen, int iArr[])
{
    char* p, * p1;
    int outLen = 1;

    iArr[0] = 0;
    p1 = inStr;
    while (NULL != (p = strchr(p1, cc)))
    {
        p1 = p + 1;
        p[0] = 0;

        if (outLen >= maxLen)
            break;
        iArr[outLen] = p1 - inStr;
        outLen++;
    }

    return outLen;
}

int NetWork::NetMkdirP(char* errStr, const char* path, int mode)
{
    if (0 == access(path, F_OK)) return 0;

    int outLen = 0;
    char buf[512], resBuf[512];
    int offset[20], off = 0;

    memset(buf, 0x00, sizeof(buf));
    memset(resBuf, 0x00, sizeof(resBuf));

    memcpy(buf, path, strlen(path) + 1);
    outLen = NetSplitStr(buf, '/', sizeof(offset), offset);

    for (int i = 0; i < outLen; ++i)
    {
        if ('\0' == *(buf + offset[i])) continue;

        off += sprintf(resBuf + off, "/%s", buf + offset[i]);
        if (0 == access(resBuf, F_OK)) continue;

        if (0 != mkdir(resBuf, mode))
        {
            sprintf(errStr, "error : %s", strerror(errno));
            return -1;
        }
    }

    return 0;
}

int NetWork::NetSendData(char* errStr, int nSocket, char* buf, int bufLen)
{
    IntToChars(buf, bufLen);
    
    int sendLen = 0;
    if((bufLen + 4) != (sendLen = NetWriteSocket(errStr, nSocket, buf, bufLen + 4)) )
    {
        return -1;
    }
    
    return sendLen - 4;
}

int NetWork::NetRecvData(char* errStr, int nSocket, char* buf, int timeOut)
{
    int ret = -1, dataLen = 4, flag = 0;
    
    for(int i = 0; i < 2; ++i)
    {
        if(i == 1 && flag == 0) /* flag防止重入 */
        {
            flag ++ ;
            dataLen = CharsToInt(buf);
        }
        
        if(timeOut <= 0)
        {
            if(dataLen != (ret = NetReadSocket(errStr, nSocket, buf, dataLen)))
            {
                return -1;
            }
        }
        else
        {
            if( dataLen == (ret = NetReadSocketTm(errStr, nSocket, buf, dataLen, timeOut)))
            {
                continue;
            }
            else if(ret == -2)
            {
                if(0 != NetCheckSocket(errStr, nSocket))
                {
                    return -1;
                }
                else 
                {
                    --i;
                    continue;
                }
            }
            else
            {
                return -1;
            }
        }
    }
            
    return dataLen;
}

int NetWork::NetSendConfirm(char* errStr, int nSocket)
{
    char buf[2] = "O";
    
    if (1 != NetWriteSocket(errStr, nSocket, buf, 1))
    {
        return -1;
    }
    
    return 0;
}

int NetWork::NetWaitConfirm(char* errStr, int nSocket)
{
    char buf[2] = { 0 };
    
    if(1 != NetReadSocket(errStr, nSocket, buf, 1))
    {
        return -1;
    }
    
    return 0;
}

int NetWork::NetError(char* errStr, const char* const preStr, int errNum)
{
    char tmpStr[256];
    
    if(0 == strerror_r(errNum, tmpStr, 255))
    {
        return sprintf(errStr, "%s%s", preStr, tmpStr);
    }
    else
    {
        return sprintf(errStr, "%s[%d]", preStr, errNum);
    }
}

int NetWork::AnetKeepAlive(char* errStr, int fd)
{
    /*  
        keepIdle        如该连接在6秒内没有任何数据往来,则进行探测
        keepInterval    探测时发包的时间间隔
        keepCount = 5   探测尝试的次数.如果第1次探测包就收到响应了,则后4次的不再发.
    */

    int nKeepIdle = 6, nKeepInterval = 1, nKeepCount = 5;
    int val = 1;
    
    if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(int)) == -1)
    {
        NetError(errStr, "Error setsockopt SO_KEEPALIVE: ",errno);
        return -1;
    }
    
    if(setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &nKeepIdle, sizeof(nKeepIdle)) < 0)
    {
        NetError(errStr, "Error setsockopt TCP_KEEPIDLE :", errno);
        return -1;
    }
    
    if(setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &nKeepCount, sizeof(nKeepCount)) < 0)
    {
        NetError(errStr, "Error setsockopt TCP_KEEPCNT : ",errno);
        return -1;
    }
    
    if(setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &nKeepInterval, sizeof(nKeepInterval)) < 0)
    {
        NetError(errStr, "Error setsockopt TCP_KEEPINTVL : ", errno);
        return -1;
    }
    return 0;
}

int NetWork::NetMakeSocket(char* errStr, int port)
{
    int nSocket, nValue = 1;
    
    if(-1 == (nSocket = socket(AF_INET, SOCK_STREAM, 0)))
    {
        NetError(errStr, "Error on create socket : ", errno);
        return -1;
    }
    
    /* 强行占用某个端口的属性 */
    nValue = 1;
    if(setsockopt(nSocket, SOL_SOCKET, SO_REUSEADDR, &nValue, sizeof(int)) == -1)
    {
        NetError(errStr, "Error on setsockopt SO_REUSEADDR: ", errno);
        close(nSocket);
        return -1;
    }
    
    if(-1 == AnetKeepAlive(errStr, nSocket))
    {
        close(nSocket);
        return -1;
    }
    
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);/* 本机所有网卡 */
    sin.sin_port = htons(port);
    
    int ret = bind(nSocket, (struct sockaddr*)&sin, sizeof(sin));
    if(-1 == ret)
    {
        NetError(errStr, "Error on bind socket: ", errno);
        return -1;
    }
    
    if(-1 == listen(nSocket, 1024))
    {
        NetError(errStr, "Error on listen socket : ", errno);
        return -1;
    }
    
    return nSocket;
}

int NetWork::NetTimeOut(char* errStr, int socket, int timeOut/*ms*/)
{
    struct timeval tv;
    fd_set r;
    int value = sizeof(int);
    
    tv.tv_sec = timeOut / 1000;
    tv.tv_usec = 1000 * (timeOut - tv.tv_sec * 1000);
    
    FD_ZERO(&r);
    FD_SET(socket, &r);
    int ret = select(socket + 1, NULL, &r, NULL, &tv);
    
    if(ret > 0)
    {
        int error = 0;
        if((-1 == getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&value) ) || (error != 0))
        {
            sprintf(errStr, "getsocket error , %s ", strerror(error));
            return -1;
        }
    }
    else if(ret == 0)
    {
        strcpy(errStr, "timeout.");
        return -1;
    }
    else
    {
        NetError(errStr, "", errno);
    }
    
    return 0;
}

int NetWork::NetAcceptSocket(char* errStr, int socket, string& clientIp, int block)
{
    struct sockaddr_in cli_addr;
    int nAccept, nFlags;
    
    if(0 == block)   /* 设置非堵塞 */
    {
        nFlags = fcntl(socket, F_GETFL, 0);
        fcntl(socket, F_SETFL, O_NONBLOCK | nFlags);
    }
    
    int nAddrSize = sizeof(cli_addr);
    memset(&cli_addr, 0, sizeof(cli_addr));
    
    if(-1 == (nAccept = accept(socket, (struct sockaddr*)&cli_addr, (socklen_t*)&nAddrSize)))
    {
        if(EAGAIN != errno)
        {
            NetError(errStr, "Error on accept : ", errno);
            return -1;
        }
        else
        {
            return -2;
        }
    }
    
    if (0 == block)   /* 还原回去 */
    {
        fcntl(socket, F_SETFL, nFlags);
    }

    clientIp.assign(inet_ntoa(cli_addr.sin_addr));
    
    return nAccept;
}

int NetWork::NetClose(int socket)
{
    if(socket == -1) return -1;
    
    return close(socket);
}

int NetWork::NetGetIpList(char* ipList, int len)
{
    char addr[20] = { 0 };
    struct ifaddrs* ifas = NULL, * ifTemp;
    int _offset = 0, _threshold = len - 16;

    if (0 != getifaddrs(&ifas))
        return -1;

    ifTemp = ifas;
    while (NULL != ifTemp)
    {
        if (NULL != ifTemp->ifa_addr)
        {
            if (AF_INET == ifTemp->ifa_addr->sa_family)
            {
                inet_ntop(AF_INET, &(((struct sockaddr_in*)(ifTemp->ifa_addr))->sin_addr), addr, sizeof(addr));

                if (strncmp(addr, "127.0.0.1", 9) != 0 && _threshold >= _offset)
                {
                    _offset += sprintf(ipList + _offset, "%s|", addr);
                }
            }
        }
        ifTemp = ifTemp->ifa_next;
    }
    freeifaddrs(ifas);

    return 0;
}

int NetWork::NetGetIdlePort(char* errStr)
{
    int nSocket = 0, nPort;

    if (-1 == (nSocket = socket(AF_INET, SOCK_STREAM, 0)))
    {
        NetError(errStr, "Error on create socket : ", errno);
        return -1;
    }

    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = htonl(INADDR_ANY);
    sin.sin_port = htons(0);

    int ret = bind(nSocket, (struct sockaddr*)&sin, sizeof(sin));
    if (-1 == ret)
    {
        close(nSocket);
        NetError(errStr, "Error on bind socket: ", errno);
        return -1;
    }

    int len = sizeof(sin);
    if (getsockname(nSocket, (struct sockaddr*)&sin, (socklen_t*)&len) != 0)
    {
        close(nSocket);
        NetError(errStr, "Error on bind socket: ", errno);
        return -1;
    }

    nPort = sin.sin_port;
    close(nSocket);

    return nPort;
}

int NetWork::NetConnect(char* errStr, int port, const string& host, int timeOut)
{
    int nSocket;
    struct sockaddr_in sin4;
    struct ifreq ifr;

    bzero(&sin4, sizeof(sin4));
    
    sin4.sin_family = AF_INET;
    sin4.sin_port = htons(port);
    
    /* 将ip转网络二进制 */
    if(1 != inet_pton(AF_INET, host.c_str(), &(sin4.sin_addr))) 
    {
        sprintf(errStr, "Error : unknow host(%s)", host.c_str());
        return -1;
    }

    if(-1 == (nSocket = socket(AF_INET, SOCK_STREAM, 0)))
    {
        NetError(errStr, "Error on creat socket : ", errno);
        return -1;
    }
    
    if (0 != NetWork::NetSetNoDelay(errStr, nSocket))
    {
        return -1;
    }

    if(timeOut <= 0)
    {
        if(-1 == connect(nSocket, (struct sockaddr*)&sin4, sizeof(sin4)))
        {
            NetError(errStr, "connect error : ", errno);
            NetClose(nSocket);
            return -1;
        }
    }
    else
    {
        char tmpStr[256];
        int flags = fcntl(nSocket, F_GETFL, 0);
        fcntl(nSocket, F_SETFL, flags | O_NONBLOCK);
        
        errno = 0;
        if(-1 == connect(nSocket, (struct sockaddr*)&sin4, sizeof(sin4)))
        {
            if((errno == EINPROGRESS) || (errno == 0))  /* 还在连接中 */
            {
                if(0 != NetTimeOut(tmpStr, nSocket, timeOut))
                {
                    NetClose(nSocket);
                    sprintf(errStr, "Error on connect socket(%s:%d), %s", host.c_str(), port, tmpStr);
                    return -1;
                }
            }
            else
            {
                NetClose(nSocket);
                NetError(errStr, "Error on socket connect : ", errno);
                return -1;
            }
        }
        fcntl(nSocket, F_SETFL, flags);
    }
    
    if(-1 == AnetKeepAlive(errStr, nSocket))
    {
        NetClose(nSocket);
        return -1;
    }
    
    return nSocket;
}

int NetWork::NetSetSendTimeo(char* errStr, int nSocket)
{
    struct timeval tv;
    
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    
    if(setsockopt(nSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0)
    {
        strcpy(errStr, "error on setsockopt(SO_SEDTIMEO)");
        return -1;
    }
    
    return 0;
}

int NetWork::NetSetNoDelay(char* errStr, int nSocket)
{
    int enable = 1;
    if(setsockopt(nSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&enable, sizeof(enable)) != 0)
    {
        strcpy(errStr, "error on setsockopt (TCP_NODELAY)");
        return -1;
    }
    return 0;
}

int NetWork::NetSetSendBuf(char* errStr, int nSocket, int bufLen)
{
    int len = bufLen;
    if(setsockopt(nSocket, SOL_SOCKET, SO_SNDBUF, (void*)&len, sizeof(len)) != 0)
    {
        strcpy(errStr, "error on setsockopt(SO_SNDBUF)");
        return  -1;
    }
    return 0;
}

int NetWork::NetSetRecvBuf(char* errStr, int nSocket, int bufLen)
{
    int len = bufLen;
    if(setsockopt(nSocket, SOL_SOCKET, SO_RCVBUF, (void*)&len, sizeof(len)) != 0)
    {
        strcpy(errStr, "error on setsockopt(SO_RCVBUF)");
        return -1;
    }
    return 0;
}

int NetWork::NetReadSocketTm(char* errStr, int nSocket, char* readBuf, int readSize, int timeOut)
{
    long sec, usec;
    int ret;
    
    struct timeval tmv;
    fd_set r;
    
    sec = timeOut / 1000;
    usec = (timeOut - sec * 1000) * 1000;
    
again_select:
    
    tmv.tv_sec = sec;
    tmv.tv_usec = usec;
    
    FD_ZERO(&r);
    FD_SET(nSocket, &r);
    
    ret = select(nSocket + 1, &r, NULL, NULL, &tmv);
    
    if(ret > 0)
    {
        return NetReadSocket(errStr, nSocket, readBuf, readSize);
    }
    else if(ret < 0)   /* on error */
    {
        if(errno == EINTR)  /* 系统中断，重新读 */
            goto again_select;
        
        sec = NetError(errStr, "error on read :", errno);
        sprintf(errStr + sec, " len = %d", readSize);
        return -1;
    }
    else
    {
        sprintf(errStr, "error on read timeOut , len = %d", readSize);
        return -2;  /* 超时 */
    }
}

int NetWork::NetReadSocket(char* errStr, int nSocket, char* readBuf, int readSize)
{
    long lRead, lLeft, lOffset;
    
    if(nSocket == -1)
    {
        sprintf(errStr, "error on read, nSocket = -1, len = %d", readSize);
    }
    
    lLeft = readSize;
    lOffset = 0;
    
    while(lLeft > 0)
    {
        again_read:
        lRead = read(nSocket, readBuf + lOffset, lLeft);
        if(lRead < 0)
        {
            if(errno == EINTR)
                goto again_read;
            
            lOffset = NetError(errStr, "error on read :", errno);
            sprintf(errStr + lOffset, " len = %ld", lLeft);
            return -1;
        }
        else if(lRead == 0)
        {
            readBuf[lOffset] = 0;
            sprintf(errStr, "error on read : no data, len = %ld, errno = %d", lOffset, errno);
            return lOffset;
        }
        
        lOffset += lRead;
        lLeft -= lRead;
    }
    
    readBuf[lOffset] = 0;
    return lOffset;
}

int NetWork::NetReadSocketNoBlock(char* errStr, int nSocket, char* readBuf, int readSize, int timeOut)
{
    long sec, usec;
    int ret, read = 0, offset = 0, readlen = readSize;

    struct timeval tmv;
    fd_set r;

    sec = timeOut / 1000;
    usec = (timeOut - sec * 1000) * 1000;

    tmv.tv_sec = sec;
    tmv.tv_usec = usec;

    while (readSize > 0)
    {
        FD_ZERO(&r);
        FD_SET(nSocket, &r);

    again_select:
        ret = select(nSocket + 1, &r, NULL, NULL, &tmv);
        if (ret < 0)   /* on error */
        {
            if (errno == EINTR)  /* 系统中断，重新读 */
                goto again_select;

            sprintf(errStr, "select error, %s", strerror(errno));
            return -1;
        }
        else
        {
            sprintf(errStr, "recv data timeout, all : %d, read : %d", readlen, offset);
            return -1;  /* 超时 */
        }

        read = NetReadSocketOne(errStr, nSocket, readBuf, readSize);
        if (read <= 0)
        {
            if (errno == EWOULDBLOCK)
            {
                continue;
            }
            sprintf(errStr, "read data error, %s", strerror(errno));
            return -1;
        }
        readSize -= read;
        offset += read;
    }
    readBuf[offset] = '\0';

    return offset;
}

int NetWork::NetReadSocketOne(char* errStr, int nSocket, char* readBuf, int readSize)
{
    long lRead, lLeft;

    if (nSocket == -1)
    {
        sprintf(errStr, "error on read, nSocket = -1, len = %d", readSize);
        return -1;
    }

    again_read:
    lRead = read(nSocket, readBuf, readSize);
    if (lRead < 0)
    {
        if (errno == EINTR)
            goto again_read;

        lLeft = NetError(errStr, "error on read :", errno);
        sprintf(errStr + lLeft, " len = %d", readSize);
        return -1;
    }
    else if (lRead == 0)
    {
        readBuf[lRead] = 0;

        sprintf(errStr, 
            "error on read : no data, len = %ld, errno = %d", 
            lRead, errno
        );
        return lRead;
    }

    readBuf[lRead] = 0;
    return lRead;
}

int NetWork::NetWriteSocket(char* errStr, int nSocket, const char* writeBuf, int writeSize)
{
    long lWrite, lLeft, lOffset;
    
    if(nSocket == -1)
    {
        sprintf(errStr, "error on write : nSocket == -1, len = %d", writeSize);
        return -1;
    }
    
    lLeft = writeSize;
    lOffset = 0;
    
    while(lLeft > 0)
    {
        
        again_write:
        
        lWrite = write(nSocket, writeBuf + lOffset, lLeft);
        
        if(lWrite < 0)
        {
            if(errno == EINTR || errno == EAGAIN)/* 系统调用 - 缓冲区满 */
                goto again_write;
            
            NetError(errStr, "error on write : ", errno);
            return -1;
        }
        else if(lWrite == 0)
        {
            strcpy(errStr, "Error on write : return = 0");
            return -1;
        }
        
        lOffset += lWrite;
        lLeft -= lWrite;
    }
    
    return lOffset;
}

int NetWork::NetCheckSocket(char* errStr, int nSocket)
{
    struct timeval tmv;
    fd_set r;
    
    if(nSocket == -1)
    {
        strcpy(errStr, "Error on check, nSocket == -1");
        return -1;
    }
    
    tmv.tv_sec = 0;
    tmv.tv_usec = 0;
    
    FD_ZERO(&r);
    FD_SET(nSocket, &r);
    
    int ret = select(nSocket + 1, &r, NULL, NULL, &tmv);
    
    if(ret == 0)
    {
        return 0;
    }
    else if(ret < 0)
    {
        NetError(errStr, "Error on select :", errno);
        return -1;
    }
    else
    {
        struct tcp_info info;
        int len = sizeof(info);
        memset(&info, 0, len);

        /*  检查tcp状态  */
        getsockopt(nSocket, IPPROTO_TCP, TCP_INFO, &info, (socklen_t* )&len);
        if((info.tcpi_state == TCP_ESTABLISHED))
        {
            return 0;
        }
        else
        {
            sprintf(errStr, "connection reset by peer, tcpi_state = %d", info.tcpi_state);
            return -1;
        }
    }
}
